/*
 * ebc_dbg.c - EBC
 * Clunix, KyungWoon Cho, cezanne@clunix.com, 2006.1
 *
 * Copyright (c) 2001-2008 by Clunix Inc. All Rights Reserved.
 */

#include "config.h"

#include "ecm.h"
#include "ebc.h"

#ifdef ECM_DEBUG

ecm_dbg_t	ebc_dbg;

static ecm_hashtable_t	idmap_hash;

typedef struct {
	void	*data;
	int	len;
} dbgkey_t;

#define INSERT_IDMAP(ID, NAME)	\
		do {	\
			ebc_ ## ID ##_t	id;	\
			ebc_get_ ## ID (NAME, &id);	\
			insert_idmap(IDMAP_ ## ID, NAME, sizeof(ebc_ ## ID ## _t), &id); \
		} while(0)

static ECM_INLINE dbgkey_t
get_idmap_key(idmap_type_t type, ecm_size_t len, ecm_ptr_t ptr)
{
	dbgkey_t	key;

	key.len = len + sizeof(idmap_type_t);
	key.data = ecm_malloc(key.len);
	*(idmap_type_t *)key.data = type;
	memcpy((char *)key.data + sizeof(idmap_type_t), ptr, len);
	return key;
}

static void
insert_idmap(idmap_type_t type, const char *name, ecm_size_t len, ecm_ptr_t ptr)
{
	dbgkey_t	key;

	key = get_idmap_key(type, len, ptr);
	ecm_hashtable_insert(idmap_hash, ecm_strdup(name), key.len, key.data);
	ecm_free(key.data);
}

char *
ebc_get_idmap(idmap_type_t type, ecm_size_t len, ecm_ptr_t ptr)
{
	dbgkey_t	key;
	char	*name;

	key = get_idmap_key(type, len, ptr);
	name = ecm_hashtable_get(idmap_hash, key.len, key.data);
	ecm_free(key.data);
	return name;
}

ECMBOOL
ebc_dbg_setup_idmap(const char *fname)
{
	char	buf[256], *line;
	idmap_type_t	idmap = IDMAP_NONE;
	FILE	*fp = fopen(fname, "r");

	if (fp == NULL)
		return ECM_FALSE;
	idmap_hash = ecm_hashtable_create(32);
	ecm_hashtable_set_autorehash(idmap_hash, ECM_TRUE);

	while ((fgets(buf, sizeof buf, fp))) {
		line = ecm_trim(buf);
		if (ecm_strcmp(line, "%ndid") == 0)
			idmap = IDMAP_ndid;
		else if (ecm_strcmp(line, "%mdid") == 0)
			idmap = IDMAP_mdid;
		else if (ecm_strcmp(line, "%ptid") == 0)
			idmap = IDMAP_ptid;
		else {
			switch (idmap) {
			case IDMAP_ndid:
				INSERT_IDMAP(ndid, line);
				break;
			case IDMAP_mdid:
				INSERT_IDMAP(mdid, line);
				break;
			case IDMAP_ptid:
				INSERT_IDMAP(ptid, line);
				break;
			case IDMAP_NONE:
				break;
			}
		}
	}
	fclose(fp);
	return ECM_TRUE;
}

static char *
dbg_ndid(ebc_ndid_t ndid)
{
	if (ndid == EBC_NDID_SELF)
		return ecm_strdup(".");
	else if (ndid == EBC_NDID_BROADCAST)
		return ecm_strdup("*");
	else if (ndid == EBC_NDID_ANYCAST)
		return ecm_strdup("?");
	else {
		char	*ndname;
		ndname = ecm_strdup(ebc_get_idmap(IDMAP_ndid, sizeof(ebc_ndid_t), &ndid));
		if (ndname == NULL)
			ecm_asprintf(&ndname, "%x", ndid);
		return ndname;
	}
}

static char *
dbg_mdid(ebc_mdid_t mdid)
{
	if (mdid == EBC_MDID_EBCD)
		return ecm_strdup("ebcd");
	else {
		char	*mdname;
		mdname = ecm_strdup(ebc_get_idmap(IDMAP_mdid, sizeof(ebc_mdid_t), &mdid));
		if (mdname == NULL)
			ecm_asprintf(&mdname, "%x", mdid);
		return mdname;
	}
}

static char *
dbg_ptid(ebc_ptid_t ptid)
{
	char	*ptname;

	if (ptid == EBC_PTID_DEFAULT)
		ptname = NULL;
	else {
		ptname = ecm_strdup(ebc_get_idmap(IDMAP_ptid, sizeof(ebc_ptid_t), &ptid));
		if (ptname == NULL)
			ecm_asprintf(&ptname, "%x", ptid);
	}
	return ptname;
}

static void
print_addr(char *buf, ebc_addr_t *paddr)
{
	char	*ndname, *mdname, *ptname;

	if (paddr == NULL) {
		sprintf(buf, "<empty>");
		return;
	}

	ndname = dbg_ndid(paddr->ndid);

	if (paddr->mdid == EBC_MDID_EBCD)
		mdname = ecm_strdup("ebcd");
	else {
		mdname = ecm_strdup(ebc_get_idmap(IDMAP_mdid, sizeof(ebc_mdid_t), &paddr->mdid));
		if (mdname == NULL)
			ecm_asprintf(&mdname, "%x", paddr->mdid);
	}

	ptname = dbg_ptid(paddr->ptid);

	if (ptname == NULL)
		ecm_sprintf(buf, "{pa/%s/%s}", ndname, mdname);
	else
		ecm_sprintf(buf, "{pa/%s/%s/%s}", ndname, mdname, ptname);
	ecm_free(ndname);
	ecm_free(mdname);
	ecm_free(ptname);
}

const char *
ebc_dbg_ndid(ebc_ndid_t ndid)
{
	static char	buf[256];
	char	*ndid_str;

	ndid_str = dbg_ndid(ndid);
	ecm_snprintf(buf, sizeof buf, "%s", ndid_str);
	ecm_free(ndid_str);
	return buf;
}

const char *
ebc_dbg_mdid(ebc_mdid_t mdid)
{
	static char	buf[256];
	char	*mdid_str;

	mdid_str = dbg_mdid(mdid);
	ecm_snprintf(buf, sizeof buf, "%s", mdid_str);
	ecm_free(mdid_str);
	return buf;
}

const char *
ebc_dbg_ptid(ebc_ptid_t ptid)
{
	static char	buf[256];
	char	*ptid_str;

	ptid_str = dbg_ptid(ptid);
	ecm_snprintf(buf, sizeof buf, "%s", ptid_str);
	ecm_free(ptid_str);
	return buf;
}

const char *
ebc_dbg_addr(ebc_addr_t *paddr)
{
	static char	buf[256];

	print_addr(buf, paddr);
	return buf;
}

const char *
ebc_dbg_pktb(ebc_pktb_t *pktb)
{
	static char	buf[256];
	char		srcbuf[256], dstbuf[256];
	ebc_msgt_t	msgt;

	if (pktb == NULL)
		return "[null]";
	print_addr(srcbuf, &pktb->pkthdr.mhdr.srcaddr);
	print_addr(dstbuf, &pktb->pkthdr.mhdr.dstaddr);

	msgt = pktb->pkthdr.mhdr.msgt;
	ecm_snprintf(buf, 256, "[%s=>%s:%s%s%s:%u:%u:%u]", srcbuf, dstbuf,
		     msgt & EBC_MSGT_REQ ? "REQ": "RES",
		     msgt & EBC_MSGT_ONEWAY ? "1": "",
		     msgt & EBC_MSGT_ERRMASK ? ",Err": "",
		     pktb->pkthdr.pktlen,
		     pktb->pkthdr.pktcnt,
		     pktb->pkthdr.pktnum);
	return buf;
}

const char *
ebc_dbg_pch(ebc_pch_t *pchp)
{
	static char	buf[256];
	char	*ndname;

	if (pchp == NULL)
		return "(null)";
	ndname = dbg_ndid(pchp->ndid);
	ecm_snprintf(buf, 256, "(%s:%s)", ecm_safe_str(ndname), ecm_safe_str(pchp->name));
	ecm_free(ndname);
	return buf;
}

void
ebc_set_debugset(ecm_uint32 dbgset)
{
	if (ebc_dbg == NULL)
		ebc_dbg = ecm_create_dbg();
	ecm_set_debugset(ebc_dbg, dbgset);
}

ECMBOOL
ebc_set_debugset_str(const char *str_dbgset)
{
	ecm_uint32	dbgset = 0;
	const char	*p = str_dbgset;

	while (*p) {
		switch (*p) {
		case 'a':
			dbgset |= DBG_EBC_ALL;
			break;
		case 'p':
			dbgset |= DBG_EBC_PACKET;
			break;
		case 'e':
			dbgset |= DBG_EBC_EXTCOMM;
			break;
		case 'b':
			dbgset |= DBG_EBC_BUFFER;
			break;
		case 'r':
			dbgset |= DBG_EBC_ROUTE;
			break;
		case 'c':
			dbgset |= DBG_EBC_CALLBACK;
			break;
		case 'n':
			dbgset |= DBG_EBC_CONN;
			break;
		case 's':
			dbgset |= DBG_EBC_SPM;
			break;
		default:
			return ECM_FALSE;
		}
		p++;
	}

	ebc_set_debugset(dbgset);
	return ECM_TRUE;
}

#endif
